package com.mtr.databoard.domain.resources;

import com.mtr.databoard.common.linqlist.LinqList;
import com.mtr.databoard.domain.components.ViewComponent;
import com.mtr.databoard.domain.components.ViewResource;
import org.bson.types.Binary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

@Component
public class ResourceStore {

    @Autowired
    private IResourceRepository resourceRepository;
    @Autowired
    private IFileContentRepository fileContentRepository;

    private static int BUFFER_SIZE = 1024;

    /**
     * 获取资源
     *
     * @param fileId
     * @return
     */
    public Resource getResource(String fileId) {
        try {

            return resourceRepository.findById(fileId).get();
        } catch (Exception ex) {
            return null;
        }
    }


    /**
     * 获取文件内容
     *
     * @param fileId
     * @return
     */
    public FileContent getFileContent(String fileId) {
        try {

            return fileContentRepository.findById(fileId).get();
        } catch (Exception ex) {
            return null;
        }
    }


    /**
     * 绑定实体
     *
     * @param fileId
     * @param entity
     */
    public void bindEntity(String fileId, ResourceEntity entity) {
        Resource resource = getResource(fileId);
        if (resource != null) {
            resource.bindEntity(entity);
            resourceRepository.save(resource);
        }
    }

    /**
     * 批量绑定实体
     *
     * @param fileIds
     * @param entity
     */
    public void bindEntity(List<String> fileIds, ResourceEntity entity) {

        for (String fileId : fileIds
        ) {
            bindEntity(fileId, entity);
        }

    }

    /**
     * 解绑实体
     *
     * @param fileId
     * @param entity
     */
    public void unbindEntity(String fileId, ResourceEntity entity) {
        Resource resource = getResource(fileId);
        if (resource != null) {
            resource.unbindEntity(entity);
            resourceRepository.save(resource);
            if (!resource.hasEntities()) {
                //没有绑定其它实体，那就删除文件
                deleteEmptyResource(fileId);
            }
        }
    }

    /**
     * 删除资源
     *
     * @param fileId
     */
    private void deleteEmptyResource(String fileId) {

        if (resourceRepository.existsById(fileId))
            resourceRepository.deleteById(fileId);
        if (fileContentRepository.existsById(fileId))
            fileContentRepository.deleteById(fileId);

    }

    /**
     * 清空无实体关联的数据
     */
    public void clearEmptyResources() {
        List<Resource> resourceList = resourceRepository.findAll();

        for (Resource resource : resourceList) {
            if (!resource.hasEntities()) {
                deleteEmptyResource(resource.getId());
            }
        }
    }


    /**
     * 解压资源到临时文件夹（从数据库中将二进制存为文件）
     *
     * @param basePath
     * @param contextId
     * @return 返回文件目录
     */
    public String releaseResources(List<ContextResource> resources, String basePath, String contextId, boolean isRebuild) throws IOException {

        String path = new File("admin-web/target/" + basePath).getAbsolutePath() + "/" + contextId;

        File dir = new File(path);
        //已存在先清空
        if (dir.exists() && isRebuild)
            deleteDir(dir);
        //重新创建目录
        dir.mkdirs();

        for (ContextResource resource :
                resources) {
            try {
                SaveResource(path, resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        return path;
    }

    /**
     * 删除目录
     *
     * @param file
     */
    private void deleteDir(File file) {
        if (file.isFile())
            file.delete();
        if (file.isDirectory()) {
            var files = file.listFiles();
            for (File _file : files) {
                deleteDir(_file);
            }
        }
    }

    /**
     * 清除资源
     *
     * @param basePath
     * @param contextId
     */
    public void clearReleasedResources(String basePath, String contextId) {

        String path = new File("admin-web/target/" + basePath).getAbsolutePath() + "/" + contextId;

        File dir = new File(path);
        //已存在先清空
        if (dir.exists())
            dir.delete();
    }


    /**
     * 保存文件到目录
     *
     * @param path
     * @param resource
     */
    private void SaveResource(String path, ContextResource resource) throws IOException {
        if (resource == null) return;
        OutputStream os = null;

        String basePath = path;
        //如果有分组则按分组处理
        if (resource.getGroupName() != null && resource.getGroupName().length() > 0)
            basePath += "/" + resource.getGroupName();
        String filePath = basePath + "/" + resource.getFileName();
        File file = new File(filePath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (file.exists()) return;

        FileContent fileContent = getFileContent(resource.getFileId());
        if (fileContent != null && fileContent.getContent() != null) {
            try {
                os = new FileOutputStream(file);
                os.write(fileContent.getContent().getData());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        try {
            //在将压缩包写入文件夹后，解压它
            //TODO  判断是否是压缩包，是的吧，以压缩包文件名为子目录解压出文件
            if (filePath.endsWith(".zip")) {
                var unzipPath = filePath.substring(0, filePath.length() - 4);
                var unzipFile = new File(unzipPath);
                if (!unzipFile.exists())
                    unzipFile.mkdirs();
                unZip(file, unzipPath);
            }
        } catch (Exception ex) {
        }

    }

    /**
     * 获取文件资源
     *
     * @param basePath
     * @param contextId
     * @param filePath
     * @return
     */
    public Binary getFileContent(String basePath, String contextId, String filePath) {

        String path = new File("admin-web/target/" + basePath).getAbsolutePath() + "/" + contextId + filePath;
        File file = new File(path);
        if (!file.exists()) return null;
        byte[] buffer = new byte[(int) file.length()];
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            is.read(buffer);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (Exception ex) {
                System.out.println("error:" + ex.toString());
            }
        }
        return new Binary(buffer);
    }

    /**
     * 获取文件路径（绝对路径）
     *
     * @param basePath
     * @param contextId
     * @param fileName
     * @return
     */
    public String getFilePath(String basePath, String contextId, String fileName) {
        String path = new File("admin-web/target/" + basePath).getAbsolutePath() + "/" + contextId + "/" + fileName;
        return path;
    }

    /**
     * 对比资源并进行绑定或者解绑操作
     *
     * @param oldres
     * @param newres
     */
    public void compareAndBindOrUnbindResource(List<ContextResource> oldres, List<ContextResource> newres, String contextId, String entityType) {
        var linqOldres = new LinqList<>(oldres);
        var linqNewres = new LinqList<>(newres);

        linqOldres.foreach(oldResource -> {
            //新的不包含则解绑
            if (linqNewres.count(r -> r.toString().equals(oldResource.toString())) == 0) {
                var resourceEntity = new ResourceEntity(entityType, oldResource.getGroupName(), contextId);
                unbindEntity(oldResource.getFileId(), resourceEntity);
            }
        });

        linqNewres.foreach(newResource -> {
            //旧的不包含则加绑
            if (linqOldres.count(r -> r.toString().equals(newResource.toString())) == 0) {
                var resourceEntity = new ResourceEntity(entityType, newResource.getGroupName(), contextId);
                bindEntity(newResource.getFileId(), resourceEntity);
            }
        });
    }


    /**
     * zip解压
     *
     * @param srcFile     zip源文件
     * @param destDirPath 解压后的目标文件夹
     * @throws RuntimeException 解压失败会抛出运行时异常
     */

    public static void unZip(File srcFile, String destDirPath) throws RuntimeException {

        long start = System.currentTimeMillis();
        // 判断源文件是否存在
        if (!srcFile.exists()) {
            //throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
            return;
        }

        // 开始解压

        ZipFile zipFile = null;
        try {

            zipFile = new ZipFile(srcFile);

            Enumeration<?> entries = zipFile.entries();

            while (entries.hasMoreElements()) {

                ZipEntry entry = (ZipEntry) entries.nextElement();

                System.out.println("解压" + entry.getName());

                // 如果是文件夹，就创建个文件夹

                if (entry.isDirectory()) {

                    String dirPath = destDirPath + "/" + entry.getName();

                    File dir = new File(dirPath);

                    dir.mkdirs();

                } else {

                    // 如果是文件，就先创建一个文件，然后用io流把内容copy过去

                    File targetFile = new File(destDirPath + "/" + entry.getName());

                    // 保证这个文件的父文件夹必须要存在

                    if (!targetFile.getParentFile().exists()) {

                        targetFile.getParentFile().mkdirs();

                    }

                    targetFile.createNewFile();

                    // 将压缩文件内容写入到这个文件中

                    InputStream is = zipFile.getInputStream(entry);

                    FileOutputStream fos = new FileOutputStream(targetFile);

                    int len;

                    byte[] buf = new byte[BUFFER_SIZE];

                    while ((len = is.read(buf)) != -1) {

                        fos.write(buf, 0, len);

                    }

                    // 关流顺序，先打开的后关闭

                    fos.close();

                    is.close();

                }

            }

            long end = System.currentTimeMillis();

            System.out.println("解压完成，耗时：" + (end - start) + " ms");

        } catch (Exception e) {

            throw new RuntimeException("unzip error from ZipUtils", e);

        } finally {

            if (zipFile != null) {

                try {

                    zipFile.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }

    }

    /**
     * 获取文件内容
     * @param resourceId 资源id
     * @param filePath 文件名，可带目录
     * @return
     * @throws IOException
     */
    public Binary getFileContent(String resourceId, String filePath) throws IOException {

        var resource = getResource(resourceId);
        String path = new File("admin-web/target/previewtemp").getAbsolutePath();


        String filedir = path+"/"+resourceId;
        File file = new File(filedir);
        if(!file.exists())
        {
            try {
                SaveResource(path,new ContextResource(resource.getId(),resource.getFileName(),resourceId));
            }catch (Exception e){
                //TODO log
            }
        }

        String fileName = filedir+filePath;
        file = new File(fileName);


        if (!file.exists()) return null;
        byte[] buffer = new byte[(int) file.length()];
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            is.read(buffer);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (Exception ex) {
                System.out.println("error:" + ex.toString());
            }
        }
        return new Binary(buffer);
    }

}
